﻿using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using LockerRack.Exceptions;

namespace LockerRack.Helpers
{
    internal class DataSerializer<T> where T : class
    {
        private readonly XmlSerializer _serializer;

        /// <summary>Default constructor.</summary>
        public DataSerializer()
        {
            _serializer = new XmlSerializer(typeof(T));
        }

        private T Deserialize(string xml)
        {
            using (TextReader reader = new StringReader(xml))
            {
                return Deserialize(reader);
            }
        }

        private T Deserialize(TextReader reader)
        {
            try
            {
                return (T)_serializer.Deserialize(reader);
            }
            finally
            {
                reader.Dispose();
            }
        }

        private XmlDocument Serialize(T shipmentvalidateresponse)
        {
            string xml = StringSerialize(shipmentvalidateresponse);
            var doc = new XmlDocument { PreserveWhitespace = true };
            doc.LoadXml(xml);
            doc = Clean(doc);
            return doc;
        }

        private string StringSerialize(T shipmentvalidateresponse)
        {
            using (TextWriter w = WriterSerialize(shipmentvalidateresponse))
            {
                return w.ToString().Trim();
            }
        }

        private TextWriter WriterSerialize(T serializable)
        {
            TextWriter w = new StringWriter();

            _serializer.Serialize(w, serializable);
            w.Flush();
            return w;
        }

        private XmlDocument Clean(XmlDocument doc)
        {
            doc.RemoveChild(doc.FirstChild);
            XmlNode first = doc.FirstChild;
            foreach (XmlNode n in doc.ChildNodes)
            {
                if (n.NodeType == XmlNodeType.Element)
                {
                    first = n;
                    break;
                }
            }
            if (first.Attributes != null)
            {
                XmlAttribute a = first.Attributes["xmlns:xsd"];
                if (a != null)
                {
                    first.Attributes.Remove(a);
                }
                a = first.Attributes["xmlns:xsi"];
                if (a != null)
                {
                    first.Attributes.Remove(a);
                }
            }
            return doc;
        }

        public T ReadFile(string file, out LockerException exception)
        {
            exception = null;
            try
            {
                string xml;
                using (var reader = new StreamReader(file))
                {
                    xml = reader.ReadToEnd();
                    reader.Close();
                }
                return Deserialize(xml);
            }
            catch (Exception ex)
            {
                var errString = string.Format("{0} xml serializer thrown an exception", typeof(T));
                exception = new LockerException(errString, ex);

            }
            return null;
        }

        public bool WriteFile(string file, T config, out LockerException exception)
        {
            exception = null;
            try
            {
                string xml = Serialize(config).OuterXml;
                using (var writer = new StreamWriter(file, false))
                {
                    writer.Write(xml.Trim());
                    writer.Flush();
                    writer.Close();
                }
                return true;
            }
            catch (Exception ex)
            {
                var errString = string.Format("{0} xml serializer thrown an exception", typeof(T));
                exception = new LockerException(errString, ex);

            }

            return false;
        }
    }
}
